package com.nc.mem.serial.graph.common;

import java.nio.ByteBuffer;
import java.util.Iterator;

import com.nc.mem.serial.Serializer;
import com.nc.mem.serial.graph.Context;
import com.nc.mem.serial.graph.GraphSerializer;
import com.nc.mem.serial.graph.Representation;
import com.nc.mem.serial.graph.asm.GraphSerializerFactory;


public class IterableSerializer extends GraphSerializer {

	static class BufferIterator implements Iterator<Object> {

		final Context c;
		ByteBuffer slice;
		final int sz;
		int ix;

		public BufferIterator(final Context c, final ByteBuffer b, final int sz, final int off, final int max) {
			super();
			this.c = c;
			final ByteBuffer duplicate = slice.duplicate();
			duplicate.position(off);
			duplicate.limit(max);
			this.slice = duplicate;
			this.sz = sz;
		}

		@Override
		public boolean hasNext() {
			final boolean ret = ix < sz;

			if (!ret) {
				c.clear();
			}

			return ret;
		}

		@Override
		public Object next() {
			ix++;

			final ByteBuffer sl = slice;
			final int s = Representation.readInt(sl);
			if (s == 1) {
				return null;
			} else if ((s & 1 << 1) != 0) {
				return c.fromId(s);
			} else {
				final Class<?> currClazz = Representation.idToClass(sl);
				final Serializer ser = GraphSerializerFactory.serializer(currClazz);

				return ser.read(sl, s);
			}
		}

		@Override
		public void remove() {

		}

	}

	@Override
	public void doRead(final ByteBuffer src, final Object obj) {

	}

	@SuppressWarnings("unchecked")
	@Override
	public void doWrite(final ByteBuffer dest, final Object obj) {
		final Iterable<Object> itr = (Iterable<Object>) obj;
		final int p = dest.position();
		dest.putInt(0);
		dest.putInt(0);

		int sz = 0;

		Class<?> compClazz = null;
		Serializer ser = null;
		Serializer curr;
		for (final Object o : itr) {
			sz++;
			if (o == null) {
				Representation.writeInt(dest, 1);
			} else {
				if (compClazz == null) {
					compClazz = o.getClass();
					ser = GraphSerializerFactory.serializer(compClazz);
				}
				curr = o.getClass() == compClazz ? ser : GraphSerializerFactory.serializer(o.getClass());
				curr.write(dest, o);
			}
		}

		dest.putInt(p, sz);
		dest.putInt(p + 4, dest.position());

	}

	@Override
	public Object instantiate(final ByteBuffer src) {
		final int off = src.position();
		final int sz = src.getInt();
		final int max = src.getInt();

		src.position(max);
		final Context c = Context.current().clone();
		return new Iterable<Object>() {

			@Override
			public Iterator<Object> iterator() {
				return new BufferIterator(c, src, sz, off, max);
			}
		};
	}

}
